package tornadoj.web;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tornadoj.util.Utils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by tinyms on 13-12-17.
 */
@WebServlet(name = "ApiServlet", urlPatterns = "/api/*")
public class ApiServlet extends HttpServlet {
    private final static Logger Log = LoggerFactory.getLogger(ApiServlet.class);

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doApi(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doApi(request, response);
    }

    private void doApi(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("application/json");
        PrintWriter pw = response.getWriter();
        String path = request.getPathInfo();
        Log.info(String.format("Access Url is %s", path));
        if (StringUtils.isNotBlank(path)) {
            String[] items = StringUtils.split(path, "/");
            List<String> methods = new ArrayList<String>();
            for (String item : items) {
                if (StringUtils.isNotBlank(item)) {
                    methods.add(item);
                }
            }
            if (methods.size() == 2) {
                String key = methods.get(0);
                String method = methods.get(1);
                ApiTarget apiTarget = ClassLoaderUtil.getApiObject(key);
                if (apiTarget != null) {
                    //valid user auth.
                    boolean next = true;

                    if (apiTarget.isAuth()) {
                        if (request.getSession().getAttribute(HttpContext.SESSION_CURRENT_USER) == null) {
                            ApiResult r = new ApiResult();
                            r.setSuccess(false);
                            r.setMessage("UnAuth");
                            r.setData("");
                            pw.println(Utils.encode(r));
                            next = false;
                        }
                    }
                    if (next) {
                        try {
                            String func_key = String.format("%s.%s", key, method);
                            final ApiFunctionTarget functionTarget = ClassLoaderUtil.getApiFunction(func_key);
                            if (functionTarget != null) {
                                if (functionTarget.isAuth()) {
                                    if (request.getSession().getAttribute(HttpContext.SESSION_CURRENT_USER) == null) {
                                        ApiResult r = new ApiResult();
                                        r.setSuccess(false);
                                        r.setMessage("UnAuth");
                                        r.setData("");
                                        pw.println(Utils.encode(r));
                                        next = false;
                                    }
                                }
                                if (next) {
                                    HttpContext context = new HttpContext();
                                    context.request = request;
                                    context.response = response;
                                    final Object result = functionTarget.getMethod().invoke(apiTarget.getInstance(), context);
                                    String json = "";
                                    if (StringUtils.isNotBlank(functionTarget.getCacheName()) && StringUtils.isNotBlank(functionTarget.getCacheKey())) {
                                        Map<String, String> httpParams = new HashMap<String, String>();
                                        if (validCacheParams(functionTarget.getCacheKey(), httpParams, request)) {
                                            String cacheKey = functionTarget.getCacheKey();
                                            for (String k : httpParams.keySet()) {
                                                cacheKey = cacheKey.replaceAll("\\{" + k + "\\}", httpParams.get(k));
                                            }
                                            json = (String) SimpleCache.getCache(cacheKey, new ICache() {
                                                public Object doInCache() {
                                                    if (functionTarget.isManualConvertToJson()) {
                                                        return result;
                                                    } else {
                                                        return Utils.encode(result);
                                                    }
                                                }
                                            });
                                        } else {
                                            if (functionTarget.isManualConvertToJson()) {
                                                json = (String) result;
                                            } else {
                                                json = Utils.encode(result);
                                            }
                                        }
                                    } else {
                                        if (functionTarget.isManualConvertToJson()) {
                                            json = (String) result;
                                        } else {
                                            json = Utils.encode(result);
                                        }

                                    }
                                    pw.println(json);
                                }
                            }
                        } catch (InvocationTargetException e) {
                            Log.warn("InvocationTargetException", e);
                            ErrorMessage(pw, e.getLocalizedMessage());
                        } catch (IllegalAccessException e) {
                            Log.warn("IllegalAccessException", e);
                            ErrorMessage(pw, e.getLocalizedMessage());
                        }
                    }

                } else {
                    ErrorMessage(pw, "ApiNotFound");
                }
            } else {
                ErrorMessage(pw, "UrlError");
            }
        } else {
            ErrorMessage(pw, "UrlError");
        }
        pw.close();
    }

    /**
     * 校验Http参数占位是否有值
     *
     * @param cacheKey
     * @param params
     * @param request
     * @return
     */
    private static boolean validCacheParams(String cacheKey, Map<String, String> params, HttpServletRequest request) {
        Pattern pattern = Pattern.compile("\\{.*?\\}");
        Matcher matcher = pattern.matcher(cacheKey);
        while (matcher.find()) {
            String paramName = matcher.group();
            paramName = paramName.substring(1, paramName.length() - 1);
            String paramValue = request.getParameter(paramName);
            if (StringUtils.isBlank(paramValue)) {
                return false;
            }
            params.put(paramName, paramValue);
        }
        return true;
    }

    private static void ErrorMessage(PrintWriter pw, String msg) {
        ApiResult r = new ApiResult();
        r.setSuccess(false);
        r.setMessage(msg);
        r.setData("");
        pw.println(Utils.encode(r));
    }
}
